package com.cgsrp.cgsrp_community.service.impl;

import com.alibaba.fastjson.JSON;
import com.cgsrp.cgsrp_baby.domain.Vaccine;
import com.cgsrp.cgsrp_community.dao.BlogFollowMapper;
import com.cgsrp.cgsrp_community.domain.BlogComment;
import com.cgsrp.cgsrp_community.domain.BlogFollow;
import com.cgsrp.utils.MinioUtils;
import com.cgsrp.utils.Result;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import com.cgsrp.cgsrp_community.domain.Blog;
import com.cgsrp.cgsrp_community.dao.BlogMapper;
import com.cgsrp.cgsrp_community.service.BlogService;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class BlogServiceImpl implements BlogService{

    @Resource
    private BlogMapper blogMapper;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private RestHighLevelClient restHighLevelClient;

    @Resource
    private BlogFollowMapper blogFollowMapper;

    @Resource
    private MinioUtils minioUtils;


    @Override
    public int deleteByPrimaryKey(Integer id) {
        return blogMapper.deleteByPrimaryKey(id);
    }

    @Override
    public int insert(Blog record) {
        return blogMapper.insert(record);
    }

    @Override
    public int insertSelective(Blog record) {
        return blogMapper.insertSelective(record);
    }

    @Override
    public Blog selectByPrimaryKey(Integer id) {
        return blogMapper.selectByPrimaryKey(id);
    }

    @Override
    public int updateByPrimaryKeySelective(Blog record) {
        return blogMapper.updateByPrimaryKeySelective(record);
    }

    @Override
    public int updateByPrimaryKey(Blog record) {
        return blogMapper.updateByPrimaryKey(record);
    }


    private boolean checkBlogIndex() {
        try {
            GetIndexRequest request = new GetIndexRequest("blog");
            return restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private List<Blog> parseBabyPics(){
        // 创建 ObjectMapper 实例
        ObjectMapper objectMapper = new ObjectMapper();

        // 禁用写入空对象和空值的特性
        objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        objectMapper.disable(SerializationFeature.WRITE_NULL_MAP_VALUES);


        try{
            List<Blog> list = getList();
            BulkRequest bulkRequest = new BulkRequest();
            bulkRequest.timeout("2m");
            for (Blog blog : list) {
                // 将 Java 对象转换为 JSON 字符串，并指定内容类型为 JSON
                String jsonString = objectMapper.writeValueAsString(blog);
                byte[] jsonData = jsonString.getBytes(StandardCharsets.UTF_8);
                bulkRequest.add(
                        new IndexRequest()
                                .index("blog")
                                .id(blog.getId().toString())
                                .source(jsonData, XContentType.JSON)
                );
            }
            BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
            return list;
        }catch (IOException e){
            throw  new RuntimeException(e);
        }
    }

    private List<Blog> getList(){
        HashOperations ops = redisTemplate.opsForHash();
        List<Blog> list = ops.values("blog");
        if (list.isEmpty()){
            list=blogMapper.list();
            for (Blog blog : list) {
                ops.put("blog",blog.getId(),blog);
            }
        }
        for (Blog blog : list) {
            List<BlogComment> comments = blog.getComments();
            ArrayList<BlogComment> blogComments = new ArrayList<>();
            ArrayList<BlogComment> newComments = new ArrayList<>();
            for (BlogComment comment : comments) {
                if (comment.getParentCommentId()!=0){
                    blogComments.add(comment);
                }else{
                    ArrayList<BlogComment> bl = new ArrayList<>();
                    comment.setChildComments(bl);
                    newComments.add(comment);
                }
            }
            for (BlogComment blogComment : blogComments) {
                for (BlogComment comment : newComments) {
                    List<BlogComment> childComments = comment.getChildComments();
                    if (comment.getId().equals(blogComment.getParentCommentId())){
                        childComments.add(blogComment);
                        comment.setChildComments(childComments);
                    }
                }
            }
            blog.setComments(newComments);
        }
        return list;
    }

    private SearchHits getHits(String searchKey,Integer userId,Integer curPage,Integer pageSize){
        try {
            SearchRequest searchRequest = new SearchRequest("blog");
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            // 分页
            sourceBuilder.from(curPage-1);
            sourceBuilder.size(pageSize);
            MatchQueryBuilder termQuery = null;

            // 构建一个布尔查询
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

            if (userId!=0){
                TermQueryBuilder userIdQuery = QueryBuilders.termQuery("creatBy", userId);
                boolQuery.must(userIdQuery);
                if (!(searchKey == null || searchKey.isEmpty())){
                    TermQueryBuilder titleQuery = QueryBuilders.termQuery("title", searchKey);
                    boolQuery.must(titleQuery);
                }
            }


            if (!(searchKey == null || searchKey.isEmpty())&&userId==0) {
                termQuery = QueryBuilders.matchQuery("title", searchKey);
            }

            HighlightBuilder highlightBuilder=new HighlightBuilder();
            highlightBuilder.requireFieldMatch(false);//多个高亮显示
            highlightBuilder.preTags("<font color='red'>");
            highlightBuilder.postTags("</font>");
            highlightBuilder.field("title");
            sourceBuilder.highlighter(highlightBuilder);
            if (userId != 0) {
                sourceBuilder.query(boolQuery);
            }else {
                sourceBuilder.query(termQuery);
            }
            sourceBuilder.timeout(new TimeValue(30, TimeUnit.SECONDS));
            searchRequest.source(sourceBuilder);
            SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            return search.getHits();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private PageInfo<Blog> getBlogs(List<Blog> list,Integer pageSize,Integer curPage){
        list.sort(Comparator.comparingInt(Blog::getRecommendationLevel).reversed());
        List<Blog> list1=new ArrayList<>();
        if (pageSize<list.size()-(curPage-1)*pageSize){
            list1= list.subList((curPage - 1) * pageSize, (curPage - 1) * pageSize + pageSize);
        }else {
            list1=list.subList((curPage - 1) * pageSize, list.size());
        }
        PageInfo<Blog> pageInfo = new PageInfo<>(list1);
        pageInfo.setTotal(list.size());
        PageHelper.startPage(curPage, pageSize);
        return pageInfo;
    }

    @Override
    public PageInfo<Blog> tlist(Integer curPage, Integer pageSize,String searchKey){
        boolean hasIndex=checkBlogIndex();
        ArrayList<Blog> blogs = new ArrayList<>();
        SearchHits hits;
        if (!hasIndex) {
            return getBlogs(parseBabyPics(),pageSize,curPage);
        }
        hits=getHits(searchKey,0,curPage,pageSize);
        try {
            for (SearchHit hit : hits) {
                String sourceAsString = hit.getSourceAsString();
                Blog blog=new ObjectMapper().readValue(sourceAsString,Blog.class);
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                HighlightField title = highlightFields.get("title");
                StringBuilder n_title = new StringBuilder();
                if (title!=null){
                    Text[] fragments = title.fragments();
                    for (Text fragment : fragments) {
                        n_title.append(fragment);
                    }
                    blog.setTitle(n_title.toString());
                }
                blogs.add(blog);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return getBlogs(blogs,pageSize,curPage);

        // HashOperations ops = redisTemplate.opsForHash();
        // List<Blog> list = ops.values("blog");
        // if (list.isEmpty()){
        //     list=blogMapper.list();
        //     for (Blog blog : list) {
        //         ops.put("blog",blog.getId(),blog);
        //     }
        // }
        // for (Blog blog : list) {
        //     List<BlogComment> comments = blog.getComments();
        //     ArrayList<BlogComment> blogComments = new ArrayList<>();
        //     ArrayList<BlogComment> newComments = new ArrayList<>();
        //     for (BlogComment comment : comments) {
        //         if (comment.getParentCommentId()!=0){
        //             blogComments.add(comment);
        //         }else{
        //             ArrayList<BlogComment> bl = new ArrayList<>();
        //             comment.setChildComments(bl);
        //             newComments.add(comment);
        //         }
        //     }
        //     for (BlogComment blogComment : blogComments) {
        //         for (BlogComment comment : newComments) {
        //             List<BlogComment> childComments = comment.getChildComments();
        //             if (comment.getId().equals(blogComment.getParentCommentId())){
        //                 childComments.add(blogComment);
        //                 comment.setChildComments(childComments);
        //             }
        //         }
        //     }
        //     blog.setComments(newComments);
        // }
        // list.sort(Comparator.comparingInt(Blog::getRecommendationLevel).reversed());
        // List<Blog> list1=new ArrayList<>();
        // if (pageSize<list.size()-(curPage-1)*pageSize){
        //     list1= list.subList((curPage - 1) * pageSize, (curPage - 1) * pageSize + pageSize);
        // }else {
        //     list1=list.subList((curPage - 1) * pageSize, list.size());
        // }
        // PageInfo<Blog> pageInfo = new PageInfo<>(list1);
        // pageInfo.setTotal(list.size());
        // PageHelper.startPage(curPage, pageSize);
        // return pageInfo;
    }


    //todo: 添加管理员端的更新推荐等级

    @Override
    public PageInfo<Blog> glist(int userId, Integer curPage, Integer pageSize,String searchKey) {
        boolean hasIndex=checkBlogIndex();
        ArrayList<Blog> blogs = new ArrayList<>();
        SearchHits hits;
        if (!hasIndex) {
            return getBlogs(parseBabyPics(),pageSize,curPage);
        }
        List<BlogFollow> blogFollows=blogFollowMapper.findByUserId(userId);
        try {
            for (BlogFollow blogFollow : blogFollows) {
                hits=getHits(searchKey,blogFollow.getBlogerId(),curPage,pageSize);
                for (SearchHit hit : hits) {
                    String sourceAsString = hit.getSourceAsString();
                    Blog blog=new ObjectMapper().readValue(sourceAsString,Blog.class);
                    Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                    HighlightField title = highlightFields.get("title");
                    StringBuilder n_title = new StringBuilder();
                    if (title!=null){
                        Text[] fragments = title.fragments();
                        for (Text fragment : fragments) {
                            n_title.append(fragment);
                        }
                        blog.setTitle(n_title.toString());
                    }
                    blogs.add(blog);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return getBlogs(blogs,pageSize,curPage);


        // HashOperations ops = redisTemplate.opsForHash();
        // List<Blog> list = ops.values("blog_follow");
        // if (list.isEmpty()){
        //     list=blogMapper.glist(userId);
        //     for (Blog blog : list) {
        //         ops.put("blog_follow",blog.getId(),blog);
        //     }
        // }
        //
        // List<Blog> list1=new ArrayList<>();
        // if (pageSize<list.size()-(curPage-1)*pageSize){
        //     list1= list.subList((curPage - 1) * pageSize, (curPage - 1) * pageSize + pageSize);
        // }else {
        //     list1=list.subList((curPage - 1) * pageSize, list.size());
        // }
        // PageInfo<Blog> pageInfo = new PageInfo<>(list1);
        // pageInfo.setTotal(list.size());
        // PageHelper.startPage(curPage, pageSize);
        // return pageInfo;
    }

    @Override
    public void increaseLevelByLike(Integer blogId) {
        try {
            blogMapper.increaseLevelByLike(blogId);
            DeleteIndexRequest request = new DeleteIndexRequest("blog");
            AcknowledgedResponse response = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);
            if (response.isAcknowledged()) {
                redisTemplate.delete("blog_follow");
                redisTemplate.delete("blog");
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void reduceLevelByLike(Integer blogId) {
        try {
            blogMapper.reduceLevelByLike(blogId);
            DeleteIndexRequest request = new DeleteIndexRequest("blog");
            AcknowledgedResponse response = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);
            if (response.isAcknowledged()) {
                redisTemplate.delete("blog_follow");
                redisTemplate.delete("blog");
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Result getBlogByUserId(int userId, Integer curPage, Integer pageSize, String searchKey) {
        List<Blog> blogs=blogMapper.selectBlogByCreatBy(userId,searchKey);
        List<Blog> list;
        if (pageSize<blogs.size()-(curPage-1)*pageSize){
            list= blogs.subList((curPage - 1) * pageSize, (curPage - 1) * pageSize + pageSize);
        }else {
            list=blogs.subList((curPage - 1) * pageSize, blogs.size());
        }
        PageInfo<Blog> pageInfo = new PageInfo<>(list);
        pageInfo.setTotal(blogs.size());
        PageHelper.startPage(curPage, pageSize);
        return Result.suc("查询成功",pageInfo);
    }

    @Override
    public Result uploadFile(MultipartFile photo) {
        try {
            String path = minioUtils.upload(photo);
            return Result.success(path);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


}
